class Tracer:
    def __init__(self, aClass):
        print('in __init__')
        self.aClass = aClass

    def __call__(self, *args, **kwargs):
        print('in __call__:')
        self.wrapped = self.aClass(*args, **kwargs)
        return self

    def __getattr__(self, name):
        print('Trace:', name)
        return getattr(self.wrapped, name)


# if __name__ == '__main__':
@Tracer
class Spam:
    def display(self):
        print('Spam!'*8)


@Tracer
class Person:
    def __init__(self, name, hours, rate):
        self.name = name
        self.hours = hours
        self.rate = rate

    def pay(self):
        return self.hours*self.rate

    # food = Spam()  # Triggers Wrapper()
    # food.display()  # Triggers __getattr__
    # print([food.fetches])
    # bob = Person('Bob', 40, 50)  # bob is really a Wrapper
    # print(bob.name)  # Wrapper embeds a Person
    # print(bob.pay())
    # print('')
    # sue = Person('Sue', rate=100, hours=60)  # sue is a different Wrapper
    # print(sue.name)  # with a different Person
    # print(sue.pay())
    # print(bob.name)  # bob has different state
    # print(bob.pay())
    # print([bob.fetches, sue.fetches])  # Wrapper attrs not traced
